import * as d3 from 'd3'
import componentConfig from '@/componentConfig';
import { UUIDGenerator } from '@/utils/uuid-generator';

export default class DragToCanvas {
    svg
    width = 30
    height = 30
    componentBaseSize = {}

    previewElement = null
    previewElementOffsetLeft = 0 // 预览元素的偏移量
    previewElementOffsetTop = 0 // 鼠标与预览元素的上偏移量

    constructor(svg) {}

    init = (svg, componentBaseSize, callback) => {
        this.svg = svg;
        const that = this
        this.componentBaseSize = componentBaseSize;
        // 创建拖拽源（组件库中的元素）
        const dragSource = d3.drag()
            .on("start", function(event, d){
                that.dragStarted(this, event, d)
            })
            .on("drag", function(event, d){
                that.dragged(this, event, d)
            })
            .on("end", function(event, d) {
                that.dragEnded(this, event, d, callback)
            });

        // 将拖拽源应用到组件库中的元素
        d3.selectAll(".component-item").call(dragSource);
    }

    // 拖拽开始
    dragStarted = (target, event, d) => {
        
        // 获取组件类型
        const componentName = d3.select(target).attr("data-component-name");
        const componentType = d3.select(target).attr("data-component-type");
        const componentSize = this.componentBaseSize[componentType];
        const component = componentConfig[componentName].component({width: componentSize.width, height: componentSize.height})

        // 创建预览元素
        this.createPreviewElement(component, componentSize, target, event);
    }
    
    // 拖拽过程中 - 移动预览元素
    dragged = (target, event, d) => {
        if (!this.previewElement) return;

        // 获取鼠标在画布上的位置
        const {x, y} = this.getTopLeftByOffset(event);

        // 更新预览元素位置
        this.updatePreviewPosition(x, y);
    }
    
    // 拖拽结束（释放鼠标时）
    dragEnded = (target, event, d, callback) => {
        // 恢复组件样式
        d3.select(target).style("opacity", 1);
        
        // 获取拖拽源的组件类型
        const componentName = d3.select(target).attr("data-component-name");
        const componentType = d3.select(target).attr("data-component-type");
        const componentSize = this.componentBaseSize[componentType];

        
        // 获取鼠标在画布上的位置
        const {x, y} = this.getTopLeftByOffset(event);

        const canvasRect = this.svg.node().getBoundingClientRect();
        const canvasX = canvasRect.left;
        const canvasY = canvasRect.top;
        const positionX = x - canvasX;
        const positionY = y - canvasY;

        this.clearPreviewElement();
        
        // 在画布上创建对应组件
       const componentElement = this.createComponent(target, componentName, componentSize, positionX, positionY);

        callback && callback(componentElement, {
            x: positionX,
            y: positionY,
            width: componentSize.width,
            height: componentSize.height
        });
    }

    createComponent = (target, componentName, componentSize, x, y) => {

        const component = componentConfig[componentName].component({width: componentSize.width, height: componentSize.height})
        const componentType = componentConfig[componentName].type;

        const componentElement = this.svg.append('g')
            .attr('class', UUIDGenerator.v4())
            .append('svg:svg')
            .attr('data-component-name', componentName)
            .attr('data-component-type', componentType)
            .attr('width', componentSize.width)
            .attr('height', componentSize.height)
            .attr('x', x)
            .attr('y', y)
            .attr('class', 'components')
            .html(component)
            
        
        return componentElement;
    }

    createPreviewElement = (component, componentSize, target, event) => {

        // 先移除可能存在的预览元素
        this.clearPreviewElement();


        const {offsetX, offsetY, targetX, targetY} = this.getSourcePointer(event, target);
        this.previewElementOffsetLeft = offsetX;
        this.previewElementOffsetTop = offsetY;
        const width = componentSize.width;
        const height = componentSize.height;


        this.previewElement = d3.select('#d3CanvasView')
            .append('div')
            .attr('id', 'dragPreviewElement')
            .style('position', 'fixed')
            .style('top', targetY + 'px')
            .style('left', targetX + 'px')
            .style('z-index', 1000)
            .style('cursor', 'grab')
            .style('width', width + 'px')
            .style('height', height + 'px')
            
        this.previewElement.append('svg:svg')
        .attr('width', width)
        .attr('height', height)
        .html(component)
    }

    updatePreviewPosition = (x, y) => {
        if (!this.previewElement) return;
        this.previewElement.style("left", x + 'px').style('top', y + 'px');
    }

    getSourcePointer = (event, target) => {
        const pointX = event?.sourceEvent?.x;
        const pointY = event?.sourceEvent?.y;
        const svgComponent = target.querySelector('.svg-component')
        const targetRect = svgComponent?.getBoundingClientRect();
        const targetX = targetRect?.left ?? 0;
        const targetY = targetRect?.top ?? 0;
        const offsetX = pointX - targetX ;
        const offsetY = pointY - targetY;
        
        return {offsetX, offsetY, targetX, targetY}
    }

    // 通过偏移量计算top、left
    getTopLeftByOffset = (event) => {
        const pointX = event?.sourceEvent?.x;
        const pointY = event?.sourceEvent?.y;
        const x = pointX - this.previewElementOffsetLeft;
        const y = pointY - this.previewElementOffsetTop;

        return {x, y}
    }

    clearPreviewElement = () => {
        if (this.previewElement) {
            this.previewElement.remove();
            this.previewElementOffsetLeft = 0;
            this.previewElementOffsetTop = 0;
        }
    }



}